/*
 * Copyright (C) 2009 M. Hautle.
 * 
 * This file is part of TrackedBean.
 * 
 * TrackedBean is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option) any later version.
 * 
 * TrackedBean is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License along with TrackedBean. If not, see <http://www.gnu.org/licenses/>.
 */
package ch.trackedbean.copier.internal;

import static ch.simpleel.ValueAccessorManager.*;
import java.beans.*;
import java.util.*;
import ch.msoftch.internal.*;
import ch.simpleel.accessors.*;
import ch.trackedbean.copier.*;

/**
 * Factory/configurator for configuring {@link BeanMapper}s manually.<br>
 * Use this factory to create and configure a {@link BeanMapper} by code.<br>
 * Consider to use the {@link BeanMapperManager} and a declarative field mapping.<br>
 * You may reuse a factory simply by creating a new mapper over {@link #createMapper(Class, Class)}.
 * 
 * @author M. Hautle
 */
public class BeanMapperConfigurator {
    /** The bean mapper to configure. */
    protected BeanMapper beanMapper;

    /** Set holding the property names of properties were no default mapping has to be defined. */
    protected final Set<String> noDefaultMapping = new HashSet<String>();

    /**
     * Creates a factory to configure a mapper for the given destination type.
     * 
     * @param dstType The destination type of the mapper
     * @return A factory to build the mapper
     */
    public static BeanMapperConfigurator create(Class dstType) {
        return create(null, dstType);
    }

    /**
     * Creates a factory to configure a mapper for the given destination type.
     * 
     * @param baseType The mapped type (may be null)
     * @param dstType The destination type of the mapper
     * @return A factory to build the mapper
     */
    public static BeanMapperConfigurator create(Class baseType, Class dstType) {
        return new BeanMapperConfigurator().createMapper(baseType, dstType);
    }

    /**
     * Creates a new mapper for the specified type.
     * 
     * @param baseType The mapped type (may be null)
     * @param dstType The destination type of the mapper
     * @return The factory
     */
    public BeanMapperConfigurator createMapper(Class baseType, Class dstType) {
        setBeanMapper(new BeanMapper(baseType, dstType));
        return this;
    }

    /**
     * Sets the mapper to configure with this
     * 
     * @param mapper The mapper to configure
     */
    public void setCurrentMapper(BeanMapper mapper) {
        beanMapper = mapper;
    }

    /**
     * Returns the mapper
     * 
     * @return The mapper
     */
    public BeanMapper getBeanMapper() {
        return beanMapper;
    }

    /**
     * @param beanMapper The beanMapper to set.
     */
    public void setBeanMapper(BeanMapper beanMapper) {
        this.beanMapper = beanMapper;
        noDefaultMapping.clear();
    }

    /**
     * Adds a simple mapping for the given property name based on the passed mapping type.
     * 
     * @param srcType The type of the source bean
     * @param srcProperty The name of the property in the source type
     * @param dstProperty The property name in the type for which the mapper is built (type on which the annotations were defined)
     * @param profile The profile to which this mapping belongs
     * @return The factory itself
     */
    public BeanMapperConfigurator addSimpleMapping(Class<?> srcType, String srcProperty, String dstProperty, String profile) {
        return addSimpleMapping(srcType, srcProperty, dstProperty, (IPropertyConverter) null, false, profile);
    }

    /**
     * Adds a root (the source object will be directly mapped using an self accessor) mapping for the given property name.<br>
     * This is a read only mapping.
     * 
     * @param srcType The type of the source bean
     * @param dstProperty The property name in the type for which the mapper is built (type on which the annotations were defined)
     * @param converter The converter to use or null
     * @param profile The profile to which this mapping belongs
     * @return The factory itself
     */
    public BeanMapperConfigurator addSimpleMapping(Class<?> srcType, String dstProperty, Class<? extends IPropertyConverter> converter, String profile) {
        return addSimpleMapping(srcType, dstProperty, createInstance(converter), profile);
    }

    /**
     * Adds a root (the source object will be directly mapped using an self accessor) mapping for the given property name.<br>
     * This is a read only mapping.
     * 
     * @param srcType The type of the source bean
     * @param dstProperty The property name in the type for which the mapper is built (type on which the annotations were defined)
     * @param converter The converter to use or null
     * @param profile The profile to which this mapping belongs
     * @return The factory itself
     */
    public BeanMapperConfigurator addSimpleMapping(Class<?> srcType, String dstProperty, IPropertyConverter converter, String profile) {
        noDefaultMapping.add(dstProperty);
        final IValueAccessor dst = getPropertyAccessor(beanMapper.getDstType(), dstProperty);
        if (converter == null && !srcType.equals(dst.getType()))
            throw new MapperInitialisationException("The source type and " + dstProperty + " must be of the same type or a converter must be passed!");
        beanMapper.addMapping(srcType, dstProperty, new SimpleMappingHolder(getSelfAccessor(), dst, converter, true, false, profile));
        return this;
    }

    /**
     * Adds a simple mapping for the given property name based on the passed mapping type.
     * 
     * @param srcType The type of the source bean
     * @param srcProperty The name of the property in the source type
     * @param dstProperty The property name in the type for which the mapper is built (type on which the annotations were defined)
     * @param readOnly Flag indicating if the mapping should be 'readonly' so that {@link IBeanMapper#copyDst2Src(Object, Object, IMappingContext)} will not be
     *            executed
     * @param profile The profile to which this mapping belongs
     * @return The factory itself
     */
    public BeanMapperConfigurator addSimpleMapping(Class<?> srcType, String srcProperty, String dstProperty, boolean readOnly, String profile) {
        return addSimpleMapping(srcType, srcProperty, dstProperty, (IPropertyConverter) null, readOnly, profile);
    }

    /**
     * Adds a simple mapping for the given property name based on the passed mapping type.
     * 
     * @param srcType The type of the source bean
     * @param srcProperty The name of the property in the source type
     * @param dstProperty The property name in the type for which the mapper is built (type on which the annotations were defined)
     * @param converter The converter to use or null
     * @param readOnly Flag indicating if the mapping should be 'readonly' so that {@link IBeanMapper#copyDst2Src(Object, Object, IMappingContext)} will not be
     *            executed
     * @param profile The profile to which this mapping belongs
     * @return The factory itself
     */
    public BeanMapperConfigurator addSimpleMapping(Class<?> srcType, String srcProperty, String dstProperty, Class<? extends IPropertyConverter> converter,
            boolean readOnly, String profile) {
        return addSimpleMapping(srcType, srcProperty, dstProperty, createInstance(converter), readOnly, profile);
    }

    /**
     * Adds a simple mapping for the given property name based on the passed mapping type.
     * 
     * @param srcType The type of the source bean
     * @param srcProperty The name of the property in the source type
     * @param dstProperty The property name in the type for which the mapper is built (type on which the annotations were defined)
     * @param converter The converter to use or null
     * @param readOnly Flag indicating if the mapping should be 'readonly' so that {@link IBeanMapper#copyDst2Src(Object, Object, IMappingContext)} will not be
     *            executed
     * @param profile The profile to which this mapping belongs
     * @return The factory itself
     */
    public BeanMapperConfigurator addSimpleMapping(Class<?> srcType, String srcProperty, String dstProperty, IPropertyConverter converter, boolean readOnly,
            String profile) {
        noDefaultMapping.add(dstProperty);
        final IValueAccessor src = getPropertyAccessor(srcType, srcProperty);
        final IValueAccessor dst = getPropertyAccessor(beanMapper.getDstType(), dstProperty);
        if (converter == null && !src.getType().equals(dst.getType()))
            throw new MapperInitialisationException("The properties " + srcProperty + " and " + dstProperty
                    + " must be of the same type or a converter must be passed!");
        beanMapper.addMapping(srcType, dstProperty, new SimpleMappingHolder(src, dst, converter, readOnly, false, profile));
        return this;
    }

    /**
     * Adds a deep mapping for the given property name based on the defined type.
     * 
     * @param srcType The type of the source bean
     * @param srcProperty The name of the property in the source type
     * @param dstProperty The property name in the type for which the mapper is built (type on which the annotations were defined)
     * @param overrideSrc Flag indicating if {@link DeepMappingHolder#copySrc2Dst(Object, Object, IMappingContext)} should override an object hold by the source
     *            property
     * @param overrideDst Flag indicating if {@link DeepMappingHolder#copyDst2Src(Object, Object, IMappingContext)} should override an object hold by the
     *            destination property
     * @param srcEvaluator The type evaluator to use for {@link DeepMappingHolder#copyToSource(Object, Object, IMappingContext)} or null
     * @param dstEvaluator The type evaluator to use for {@link DeepMappingHolder#copyToDestination(Object, Object, IMappingContext)} or null
     * @param readOnly Flag indicating if the mapping should be 'readonly' so that {@link IBeanMapper#copyDst2Src(Object, Object, IMappingContext)} will not be
     *            executed
     * @param lazy Flag indicating if the property should be fetched lazy
     * @param profile The profile to which this mapping belongs
     * @return The factory itself
     */
    public BeanMapperConfigurator addDeepMapping(Class<?> srcType, String srcProperty, String dstProperty, boolean overrideSrc, boolean overrideDst,
            Class<? extends ITypeEvaluator> srcEvaluator, Class<? extends ITypeEvaluator> dstEvaluator, boolean readOnly, boolean lazy, String profile) {
        return addDeepMapping(srcType, srcProperty, dstProperty, overrideSrc, overrideDst, createInstance(srcEvaluator), createInstance(dstEvaluator),
                readOnly, lazy, profile);
    }

    /**
     * Adds a deep mapping for the given property name based on the defined type.
     * 
     * @param srcType The type of the source bean
     * @param srcProperty The name of the property in the source type
     * @param dstProperty The property name in the type for which the mapper is built (type on which the annotations were defined)
     * @param overrideSrc Flag indicating if {@link DeepMappingHolder#copySrc2Dst(Object, Object, IMappingContext)} should override an object hold by the source
     *            property
     * @param overrideDst Flag indicating if {@link DeepMappingHolder#copyDst2Src(Object, Object, IMappingContext)} should override an object hold by the
     *            destination property
     * @param srcEvaluator The type evaluator to use for {@link DeepMappingHolder#copyToSource(Object, Object, IMappingContext)} or null
     * @param dstEvaluator The type evaluator to use for {@link DeepMappingHolder#copyToDestination(Object, Object, IMappingContext)} or null
     * @param readOnly Flag indicating if the mapping should be 'readonly' so that {@link IBeanMapper#copyDst2Src(Object, Object, IMappingContext)} will not be
     *            executed
     * @param lazy Flag indicating if the property should be fetched lazy
     * @param profile The profile to which this mapping belongs
     * @return The factory itself
     */
    public BeanMapperConfigurator addDeepMapping(Class<?> srcType, String srcProperty, String dstProperty, boolean overrideSrc, boolean overrideDst,
            ITypeEvaluator srcEvaluator, ITypeEvaluator dstEvaluator, boolean readOnly, boolean lazy, String profile) {
        noDefaultMapping.add(dstProperty);
        final IValueAccessor srcProp = getPropertyAccessor(srcType, srcProperty);
        final IValueAccessor dstProp = getPropertyAccessor(beanMapper.getDstType(), dstProperty);
        beanMapper.addMapping(srcType, dstProperty, new DeepMappingHolder(srcProp, dstProp, overrideSrc, overrideDst, srcEvaluator, dstEvaluator, readOnly,
                lazy, profile));
        return this;
    }

    /**
     * Adds a deep path mapping for the given property name based on the defined {@link IBeanMapper#getMappedBaseType()}.
     * 
     * @param srcType The type of the source bean
     * @param path The path to the source property
     * @param targetType The type of the value reurned by the el expression
     * @param dstProperty The property name in the type for which the mapper is built (type on which the annotations were defined)
     * @param overrideSrc Flag indicating if {@link DeepMappingHolder#copySrc2Dst(Object, Object, IMappingContext)} should override an object hold by the source
     *            property
     * @param overrideDst Flag indicating if {@link DeepMappingHolder#copyDst2Src(Object, Object, IMappingContext)} should override an object hold by the
     *            destination property
     * @param srcEvaluator The type evaluator to use for {@link DeepMappingHolder#copyToSource(Object, Object, IMappingContext)} or null
     * @param dstEvaluator The type evaluator to use for {@link DeepMappingHolder#copyToDestination(Object, Object, IMappingContext)} or null
     * @param readOnly Flag indicating if the mapping should be 'readonly' so that {@link IBeanMapper#copyDst2Src(Object, Object, IMappingContext)} will not be
     *            executed
     * @param lazy Flag indicating if the property should be fetched lazy
     * @param profile The profile to which this mapping belongs
     * @return The factory itself
     */
    public BeanMapperConfigurator addDeepPathMapping(Class<?> srcType, String path, Class targetType, String dstProperty, boolean overrideSrc,
            boolean overrideDst, Class<? extends ITypeEvaluator> srcEvaluator, Class<? extends ITypeEvaluator> dstEvaluator, boolean readOnly, boolean lazy,
            String profile) {
        return addDeepPathMapping(srcType, path, targetType, dstProperty, overrideSrc, overrideDst, createInstance(srcEvaluator), createInstance(dstEvaluator),
                readOnly, lazy, profile);
    }

    /**
     * Adds a deep path mapping for the given property name based on the defined {@link IBeanMapper#getMappedBaseType()}.
     * 
     * @param srcType The type of the source bean
     * @param path The path to the source property
     * @param targetType The type of the value returned by the el expression
     * @param dstProperty The property name in the type for which the mapper is built (type on which the annotations were defined)
     * @param overrideSrc Flag indicating if {@link DeepMappingHolder#copySrc2Dst(Object, Object, IMappingContext)} should override an object hold by the source
     *            property
     * @param overrideDst Flag indicating if {@link DeepMappingHolder#copyDst2Src(Object, Object, IMappingContext)} should override an object hold by the
     *            destination property
     * @param srcEvaluator The type evaluator to use for {@link DeepMappingHolder#copyToSource(Object, Object, IMappingContext)} or null
     * @param dstEvaluator The type evaluator to use for {@link DeepMappingHolder#copyToDestination(Object, Object, IMappingContext)} or null
     * @param readOnly Flag indicating if the mapping should be 'readonly' so that {@link IBeanMapper#copyDst2Src(Object, Object, IMappingContext)} will not be
     *            executed
     * @param lazy Flag indicating if the property should be fetched lazy
     * @param profile The profile to which this mapping belongs
     * @return The factory itself
     */
    public BeanMapperConfigurator addDeepPathMapping(Class<?> srcType, String path, Class<?> targetType, String dstProperty, boolean overrideSrc,
            boolean overrideDst, ITypeEvaluator srcEvaluator, ITypeEvaluator dstEvaluator, boolean readOnly, boolean lazy, String profile) {
        noDefaultMapping.add(dstProperty);
        final IValueAccessor dstProp = getPropertyAccessor(beanMapper.getDstType(), dstProperty);
        beanMapper.addMapping(srcType, dstProperty, new DeepMappingHolder(getELAccessor(path, targetType), dstProp, overrideSrc, overrideDst, srcEvaluator,
                dstEvaluator, readOnly, lazy, profile));
        return this;
    }

    /**
     * Adds a collection mapping for the given property name based on the bassed type.
     * 
     * @param srcType The type of the source bean
     * @param srcProperty The name of the property in the source type
     * @param dstProperty The property name in the type for which the mapper is built (type on which the annotations were defined)
     * @param dstType The content type of the destination collection
     * @param lazy Flag indicating if the property should be fetched lazy
     * @param profile The profile to which this mapping belongs
     * @return The factory itself
     */
    public BeanMapperConfigurator addStaticCollectionMapping(Class<?> srcType, String srcProperty, String dstProperty, Class<?> dstType, boolean lazy,
            String profile) {
        return addCollectionMapping(srcType, srcProperty, dstProperty, new StaticEvaluator(dstType), lazy, profile);
    }

    /**
     * Adds a path collection mapping for the given property name based on the passed type.
     * 
     * @param srcType The type of the source bean
     * @param path The path to the source property
     * @param targetType The type of the value returned by the el expression
     * @param dstProperty The property name in the type for which the mapper is built (type on which the annotations were defined)
     * @param dstType The content type of the destination collection
     * @param lazy Flag indicating if the property should be fetched lazy
     * @param profile The profile to which this mapping belongs
     * @return The factory itself
     */
    public BeanMapperConfigurator addStaticCollectionMapping(Class<?> srcType, String path, Class targetType, String dstProperty, Class<?> dstType,
            boolean lazy, String profile) {
        return addCollectionMapping(srcType, path, targetType, dstProperty, new StaticEvaluator(dstType), lazy, profile);
    }

    /**
     * Adds a collection mapping for the given property name based on the bassed type.
     * 
     * @param srcType The type of the source bean
     * @param srcProperty The name of the property in the source type
     * @param dstProperty The property name in the type for which the mapper is built (type on which the annotations were defined)
     * @param srcEvaluator The type evaluator to use for {@link DeepMappingHolder#copyToSource(Object, Object, IMappingContext)}
     * @param lazy Flag indicating if the property should be fetched lazy
     * @param profile The profile to which this mapping belongs
     * @return The factory itself
     */
    public BeanMapperConfigurator addCollectionMapping(Class<?> srcType, String srcProperty, String dstProperty, Class<? extends ITypeEvaluator> srcEvaluator,
            boolean lazy, String profile) {
        return addCollectionMapping(srcType, srcProperty, dstProperty, createInstance(srcEvaluator), lazy, profile);
    }

    /**
     * Adds a path collection mapping for the given property name based on the passed type.
     * 
     * @param srcType The type of the source bean
     * @param path The path to the source property
     * @param targetType The type of the value returned by the el expression
     * @param dstProperty The property name in the type for which the mapper is built (type on which the annotations were defined)
     * @param srcEvaluator The type evaluator to use for {@link DeepMappingHolder#copyToSource(Object, Object, IMappingContext)}
     * @param lazy Flag indicating if the property should be fetched lazy
     * @param profile The profile to which this mapping belongs
     * @return The factory itself
     */
    public BeanMapperConfigurator addCollectionMapping(Class<?> srcType, String path, Class targetType, String dstProperty,
            Class<? extends ITypeEvaluator> srcEvaluator, boolean lazy, String profile) {
        return addCollectionMapping(srcType, path, targetType, dstProperty, createInstance(srcEvaluator), lazy, profile);
    }

    /**
     * Adds a collection mapping for the given property name based on the bassed type.
     * 
     * @param srcType The type of the source bean
     * @param srcProperty The name of the property in the source type
     * @param dstProperty The property name in the type for which the mapper is built (type on which the annotations were defined)
     * @param srcEvaluator The type evaluator to use for {@link DeepMappingHolder#copyToSource(Object, Object, IMappingContext)}
     * @param lazy Flag indicating if the property should be fetched lazy
     * @param profile The profile to which this mapping belongs
     * @return The factory itself
     */
    public BeanMapperConfigurator addCollectionMapping(Class<?> srcType, String srcProperty, String dstProperty, ITypeEvaluator srcEvaluator, boolean lazy,
            String profile) {
        noDefaultMapping.add(dstProperty);
        final IValueAccessor srcProp = getPropertyAccessor(srcType, srcProperty);
        final IValueAccessor dstProp = getPropertyAccessor(beanMapper.getDstType(), dstProperty);
        beanMapper.addMapping(srcType, dstProperty, new CollectionMappingHolder(srcProp, dstProp, srcEvaluator, lazy, profile));
        return this;
    }

    /**
     * Adds a path collection mapping for the given property name based on the passed type.
     * 
     * @param srcType The type of the source bean
     * @param path The path to the source property
     * @param targetType The type of the value returned by the el expression
     * @param dstProperty The property name in the type for which the mapper is built (type on which the annotations were defined)
     * @param srcEvaluator The type evaluator to use for {@link DeepMappingHolder#copyToSource(Object, Object, IMappingContext)}
     * @param lazy Flag indicating if the property should be fetched lazy
     * @param profile The profile to which this mapping belongs
     * @return The factory itself
     */
    public BeanMapperConfigurator addCollectionMapping(Class<?> srcType, String path, Class<?> targetType, String dstProperty, ITypeEvaluator srcEvaluator,
            boolean lazy, String profile) {
        noDefaultMapping.add(dstProperty);
        final IValueAccessor dstProp = getPropertyAccessor(beanMapper.getDstType(), dstProperty);
        beanMapper.addMapping(srcType, dstProperty, new CollectionMappingHolder(getELAccessor(path, targetType), dstProp, srcEvaluator, lazy, profile));
        return this;
    }

    /**
     * Adds a path mapping.
     * 
     * @param srcType The type of the source bean
     * @param property The destination property
     * @param src The source class
     * @param path The path starting at the source class
     * @param readOnly Flag indicating if the mapping should be 'readonly' so that {@link IBeanMapper#copyDst2Src(Object, Object, IMappingContext)} will not be
     *            executed
     * @param profile The profile to which this mapping belongs
     * @return The factory itself
     */
    public BeanMapperConfigurator addPathMapping(Class<?> srcType, String property, Class src, String path, boolean readOnly, String profile) {
        return addPathMapping(srcType, property, src, path, (IPropertyConverter) null, readOnly, profile);
    }

    /**
     * Adds a path mapping.
     * 
     * @param srcType The type of the source bean
     * @param property The destination property
     * @param src The source class
     * @param path The path starting at the source class
     * @param converter The converter to use or null
     * @param readOnly Flag indicating if the mapping should be 'readonly' so that {@link IBeanMapper#copyDst2Src(Object, Object, IMappingContext)} will not be
     *            executed
     * @param profile The profile to which this mapping belongs
     * @return The factory itself
     */
    public BeanMapperConfigurator addPathMapping(Class<?> srcType, String property, Class src, String path, Class<? extends IPropertyConverter> converter,
            boolean readOnly, String profile) {
        return addPathMapping(srcType, property, src, path, createInstance(converter), readOnly, profile);
    }

    /**
     * Adds a path mapping.
     * 
     * @param srcType The type of the source bean
     * @param property The destination property
     * @param src The source class
     * @param path The path starting at the source class
     * @param converter The converter to use or null
     * @param readOnly Flag indicating if the mapping should be 'readonly' so that {@link IBeanMapper#copyDst2Src(Object, Object, IMappingContext)} will not be
     *            executed
     * @param profile The profile to which this mapping belongs
     * @return The factory itself
     */
    public BeanMapperConfigurator addPathMapping(Class<?> srcType, String property, Class src, String path, IPropertyConverter converter, boolean readOnly,
            String profile) {
        noDefaultMapping.add(property);
        final IValueAccessor dstDesc = getPropertyAccessor(beanMapper.getDstType(), property);
        beanMapper.addMapping(srcType, property, new SimpleMappingHolder(getELAccessor(path, Object.class), dstDesc, converter, readOnly, false, profile));
        return this;
    }

    /**
     * Adds a default mapping of the given property if it's not allready mapped.
     * 
     * @param property The destination property
     * @param profile The profile to which this mapping belongs
     * @return The factory itself
     */
    public BeanMapperConfigurator addDefaultMapping(String property, String profile) {
        return addDefaultMapping(property, false, profile);
    }

    /**
     * Adds a default mapping of the given property if it's not allready mapped.
     * 
     * @param property The destination property
     * @param readOnly Flag indicating if the mapping should be 'readonly' so that {@link IBeanMapper#copyDst2Src(Object, Object, IMappingContext)} will not be
     *            executed
     * @param profile The profile to which this mapping belongs
     * @return The factory itself
     */
    public BeanMapperConfigurator addDefaultMapping(String property, boolean readOnly, String profile) {
        final Class base = beanMapper.getMappedBaseType();
        if (base != null && !noDefaultMapping.contains(property))
            addSimpleMapping(base, property, property, readOnly, profile);
        return this;
    }

    /**
     * Adds the default mappings for the base type if the property is not allready mapped for the type and if it's not on the ignore list.
     * 
     * @param profile The profile to which the mappings will belong
     * @param ignore The properties to ignore
     * @return The factory itself
     */
    public BeanMapperConfigurator addDefaultMappings(String profile, String... ignore) {
        return addDefaultMappings(beanMapper.getMappedBaseType(), false, profile, ignore);
    }

    /**
     * Adds the default mappings for the given type if the property is not allready mapped for the type and if it's not on the ignore list.
     * 
     * @param type The base type
     * @param readOnly Flag indicating if the mapping should be 'readonly' so that {@link IBeanMapper#copyDst2Src(Object, Object, IMappingContext)} will not be
     *            executed
     * @param profile The profile to which the mappings will belong
     * @param ignore The properties to ignore
     * @return The factory itself
     */
    public BeanMapperConfigurator addDefaultMappings(Class type, boolean readOnly, String profile, String... ignore) {
        try {
            final Set<String> exclude = new HashSet<String>(noDefaultMapping);
            Collections.addAll(exclude, ignore);
            // process all properties of the specified type
            for (PropertyDescriptor d : Introspector.getBeanInfo(type).getPropertyDescriptors()) {
                final String name = d.getName();
                if (!exclude.contains(name))
                    addSimpleMapping(type, name, name, readOnly, profile);
            }
            return this;
        } catch (IntrospectionException e) {
            throw new MapperInitialisationException("Error while adding default properties", e);
        }
    }

    /**
     * Adds the property to the ignore list.<br>
     * Ensures that a call of {@link #addDefaultMapping(String,String)} does nothig.
     * 
     * @param property The property name
     */
    public void ignoreMapping(String property) {
        noDefaultMapping.add(property);
    }

    /**
     * Creates an instance of the given class.
     * 
     * @param <T> The type
     * @param type The type or null
     * @return An instance of the given type or null
     */
    @SuppressWarnings("null")
    protected <T> T createInstance(Class<T> type) {
        try {
            return type != null ? type.newInstance() : null;
        } catch (Exception e) {
            throw new MapperInitialisationException("Error while instanciating " + type.getName() + "!", e);
        }
    }

    /**
     * Cleans up the configurator.
     */
    public void cleanUp() {
        noDefaultMapping.clear();
    }

    /**
     * Simple evaluator returning an defined type.
     * 
     * @author M. Hautle
     */
    private static class StaticEvaluator implements ITypeEvaluator {
        /** The type to return */
        private final Class<?> type;

        /**
         * Default constructor.
         * 
         * @param type The type
         */
        public StaticEvaluator(Class<?> type) {
            this.type = type;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public Class<?> getCorrespondingType(Object src) throws TechnicalException {
            return type;
        }
    }
}
